package org.net.device.snmp;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;


import org.net.device.info.deviceinfo;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;

import org.snmp4j.transport.AbstractTransportMapping;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TreeEvent;
import org.snmp4j.util.TreeListener;
import org.snmp4j.util.TreeUtils;

public class SnmpOperator {
	private String host;
	private String transport = "udp";
	private int	port = 161;
	private int version = SnmpConstants.version2c;
	private OctetString community = new OctetString("public");
	private int retrys = 3;
	private int timeout = 1000;
	private int pduType = PDU.GETNEXT;
	private int maxRepetitions = 10;
	private int nonRepeaters = 0;
	private int maxSizeResponsePDU = 65535;
	private Vector vbs = new Vector();
	
	private Snmp snmp = null;
	private PDU request = null;
	private Target target = null;
	private long times;
	
	
	public SnmpOperator() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public SnmpOperator(String host, int port, int version, OctetString community) {
		super();
		this.host = host;
		this.port = port;
		this.version = version;
		this.community = community;
	}
	
	public SnmpOperator(String host, int port, int version, OctetString community, int pduType) {
		super();
		this.host = host;
		this.port = port;
		this.version = version;
		this.community = community;
		this.pduType = pduType;
	}

	private Snmp createSnmpSession() throws IOException {
	    AbstractTransportMapping transport;
	    if (getAddress(host) instanceof TcpAddress) {
	      transport = new DefaultTcpTransportMapping();
	    }
	    else {
	      transport = new DefaultUdpTransportMapping();
	    }		
		Snmp snmp = new Snmp(transport);
		return snmp;
	}

	private Target createTarget() {
		CommunityTarget target = new CommunityTarget();
		target.setCommunity(community);
		return target;
    }
	
  public PDU createPDU(Target target) {
	 PDU request = new PDU();
     request.setType(pduType);
     return request;
  }


	private Address getAddress(String transportAddress) {
		if (transportAddress.indexOf('/') < 0) {
			transportAddress += "/"+port;
		}
		if (transport.equalsIgnoreCase("udp")) {
		    return new UdpAddress(transportAddress);
		}
		else if (transport.equalsIgnoreCase("tcp")) {
			return new TcpAddress(transportAddress);
		}
		throw new IllegalArgumentException("Unknown transport "+transport);
	}

	private boolean init() {
		try {
			snmp = createSnmpSession();
			target = createTarget();
			target.setVersion(version);
			target.setAddress(getAddress(host));
			target.setRetries(retrys);
			target.setTimeout(timeout);
			target.setMaxSizeRequestPDU(maxSizeResponsePDU);
			snmp.listen();

			request = createPDU(target);
			if (request.getType() == PDU.GETBULK) {
				request.setMaxRepetitions(maxRepetitions);
				request.setNonRepeaters(nonRepeaters);
			}
			for (int i = 0; i < vbs.size(); i++) {
				request.add((VariableBinding) vbs.get(i));
				//System.out.println("vbs is "+vbs.get(i)+"  "+vbs.size());
			}			
			return true;
		}
		catch (Exception e) {
			return false;
		}
	}
	


	
	public HashMap get() throws IOException {
		PDU response;
		HashMap hm = null;
		init();
		ResponseEvent responseEvent;
		long startTime = System.currentTimeMillis();
		responseEvent = null;
		responseEvent = snmp.send(request, target);
		
		if (responseEvent != null) {
			response = null;
			response = responseEvent.getResponse();
			if (response != null) {
				if (response.getVariableBindings().size() > 0)  
					hm = new HashMap();
				//for (int j = 0;j < this.request.getVariableBindings().size();j++) {
				//System.out.println("request oid is "+this.request.getVariableBindings().get(j).toString());
				//}
				//System.out.println(response.toString());
				for (int i = 0 ;i < response.getVariableBindings().size();i++) {				
					String pause=response.getVariableBindings().get(i).toString();
					String getvalue=pause.substring( pause.indexOf(" = ")+2);
					String oid=pause.substring(0,pause.indexOf(" ="));
					//System.out.println("oid is "+oid+"   getvalue is "+getvalue);
					hm.put(oid.trim(),getvalue.trim());
				}
			} else {
				System.out.println(new Date()+"  response is null.");
			}
			times = System.currentTimeMillis() - startTime;
		}
		snmp.close();
		return hm;		
	}
	
	public List gets() throws IOException {
		init();
		return null;
	}	
	
	public HashMap walk() throws IOException {
		init();
		HashMap snapshot = new HashMap();
		walk(snmp, request, target, snapshot);		
		return snapshot;
	}
	
	private void walk(Snmp snmp, PDU request, Target target, final Map snapshot)
			throws IOException {
		request.setNonRepeaters(0);

		final WalkCounts counts = new WalkCounts();
		final long startTime = System.currentTimeMillis();
		TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory());
		TreeListener treeListener = new TreeListener() {
			public boolean next(TreeEvent e) {
				counts.requests++;
				if (e.getVariableBindings() != null) {
					VariableBinding[] vbs = e.getVariableBindings();
					counts.objects += vbs.length;
					for (int i = 0; i < vbs.length; i++) {
						if (snapshot != null) {
							String getvalue=vbs[i].getVariable().toString();
							String oid= vbs[i].getOid().toString();
							//System.out.println("oid is "+oid+"   getvalue is "+getvalue);
							snapshot.put(oid.trim(),getvalue.trim());							
						}
					}
				}
				return true;
			}

			public void finished(TreeEvent e) {
				if ((e.getVariableBindings() != null)
						&& (e.getVariableBindings().length > 0)) {
					next(e);
				}
				times = System.currentTimeMillis() - startTime;
				if (e.isError()) {
					System.err
							.println("The following error occurred during walk:");
					System.err.println(e.getErrorMessage());
				}
				synchronized (this) {
					this.notify();
				}
			}
		};
		synchronized (treeListener) {
			OID rootOID = null;
			for (int i =0;i < request.size();i++) {
				rootOID = request.get(i).getOid();
				//System.out.println(rootOID);
				treeUtils.getSubtree(target, rootOID, null, treeListener);
				try {
					treeListener.wait();
					//System.out.println("unlock : "+rootOID);
				} catch (InterruptedException ex) {
					System.err.println("Tree retrieval interrupted: "
						+ ex.getMessage());
					Thread.currentThread().interrupt();
				}
			}
		}
	}
	
	public OctetString getCommunity() {
		return community;
	}
	public void setCommunity(String community) {
		this.community = new OctetString(community);
	}	
	public void setCommunity(OctetString community) {
		this.community = community;
	}
	public String getHost() {
		return host.toString();
	}
	public void setHost(String host) {
		this.host = host;
	}
	public int getMaxRepetitions() {
		return maxRepetitions;
	}
	public void setMaxRepetitions(int maxRepetitions) {
		this.maxRepetitions = maxRepetitions;
	}
	public int getMaxSizeResponsePDU() {
		return maxSizeResponsePDU;
	}
	public void setMaxSizeResponsePDU(int maxSizeResponsePDU) {
		this.maxSizeResponsePDU = maxSizeResponsePDU;
	}
	public int getNonRepeaters() {
		return nonRepeaters;
	}
	public void setNonRepeaters(int nonRepeaters) {
		this.nonRepeaters = nonRepeaters;
	}
	public int getPduType() {
		return pduType;
	}
	public void setPduType(int pduType) {
		this.pduType = pduType;
	}
	public int getPort() {
		return port;
	}
	public void setPort(int port) {
		this.port = port;
	}
	public int getRetrys() {
		return retrys;
	}
	public void setRetrys(int retrys) {
		this.retrys = retrys;
	}
	public int getTimeout() {
		return timeout;
	}
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}
	public int getVersion() {
		return version;
	}
	public void setVersion(int version) {
		this.version = version;
	}

	public String getTransport() {
		return transport;
	}

	public void setTransport(String transport) {
		this.transport = transport;
	}

	public Vector getVbs() {
		return vbs;
	}

	public void setVbs(Vector vbs) {
		for (int i = 0;i<vbs.size();i++) {
			this.vbs.add(new VariableBinding(new OID((String)vbs.get(i))));
			
			
		}
	}
	
	public void addOID(String value) {
		vbs.add(new VariableBinding(new OID(value)));
	}

	public long getTimes() {
		return times;
	}
	
	public HashMap getValue(deviceinfo object,Vector oids,int op) {
		SnmpOperator so = new SnmpOperator();
		so.setHost(object.getIp());
		so.setCommunity(object.getRcommunity());
		so.setVersion(object.getVersion());
		so.setPduType(op);
		so.setVbs(oids);
		try {
			if (op == PDU.GET)
				return so.get();
			else
				return so.walk();
		}
		catch (IOException e) {
			return null;
		}
	}
}

class WalkCounts {
    public int requests;
    public int objects;
  }
